using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using Microsoft.Data.Schema.Extensibility;
using Microsoft.Data.Schema.SchemaModel;
using Microsoft.Data.Schema.ScriptDom.Sql;
using Microsoft.Data.Schema.Sql.SchemaModel;
using Microsoft.Data.Schema.Sql;
using Microsoft.Data.Schema.StaticCodeAnalysis;
using System.Text.RegularExpressions;

using System.Linq;

namespace Neznayka
{
    /// <summary>
    /// This is a SQL rule which returns a warning message 
    /// whenever there is a ........................................
    /// This rule only applies to .................................
    /// </summary>

    [DatabaseSchemaProviderCompatibility(typeof(SqlDatabaseSchemaProvider))]
    [DataRuleAttribute(
        NeznaykaConstants.NameSpace,
        NeznaykaConstants.EnforcePrimaryKeyRuleId,
        NeznaykaConstants.ResourceBaseName,
        NeznaykaConstants.EnforcePrimaryKey_RuleName,
        NeznaykaConstants.CategoryDatabaseStructures,
        DescriptionResourceId = NeznaykaConstants.EnforcePrimaryKey_ProblemDescription)]
    [SupportedElementType(typeof(ISqlTable))]
    public class EnforcePrimaryKeyRule : StaticCodeAnalysisRule
    {
        #region Overrides
        /// <summary>
        /// Analyze the model element
        /// </summary>
        public override IList<DataRuleProblem> Analyze(DataRuleSetting ruleSetting, DataRuleExecutionContext context)
        {

            // (Re)-Load Environment settings
            List<DataRuleProblem> problems;
            SqlSchemaModel sqlSchemaModel;
            ISqlModelElement sqlElement;
            TSqlFragment sqlFragment;
            DMVRuleSetup.RuleSetup(context, out problems, out sqlSchemaModel, out sqlElement, out sqlFragment);

            // Refresh cached index/constraints lists from Model
            DMVSettings.RefreshConstraintsAndIndexesCache(sqlSchemaModel);

            // Get Database Schema and name of this model element.
            string owningObjectSchema = sqlElement.Name.Parts[0];
            string owningObjectTable = sqlElement.Name.Parts[1];

            // Find (any) matching clustered object key
            List<TSqlFragment> issues = new List<TSqlFragment>();
            // we're not storing externally defined pk's so don't need to exclude based on that basis.
            // or do we?
            List<ISqlPrimaryKeyConstraint> pks = ModelIndexAndKeysUtils.getPrimaryKeys(owningObjectSchema, owningObjectTable);
            //If we dont find a primary key add this table definition to the list of problems.
            if (pks.Count == 0) {
                issues.Add(sqlFragment);
            }

            // Create problems for each object
            foreach (TSqlFragment issue in issues)
            {
                DataRuleProblem problem = new DataRuleProblem(this,
                                            String.Format(CultureInfo.CurrentCulture, this.RuleProperties.Description, SqlRuleUtils.GetElementName(sqlSchemaModel, sqlElement)),
                                            sqlElement);

                SqlRuleUtils.UpdateProblemPosition(problem, issue.StartOffset, issue.FragmentLength);
                problems.Add(problem);
            }

            return problems;
        }

        #endregion    
 
    }
}
